package com.kingtao.rpc;

import com.kingtao.entry.LogEntry;
import com.kingtao.entry.Node;
import com.kingtao.enums.NodeStatusEnums;
import com.kingtao.message.*;
import com.kingtao.protocol.MessageCodec;
import com.kingtao.protocol.ProtocolFrameDecoder;
import com.kingtao.service.Impl.LogServiceImpl;
import com.kingtao.service.Impl.NodeGroupServiceImpl;
import com.kingtao.session.GroupSessionFactory;
import com.kingtao.session.GroupSessionImpl;
import com.kingtao.util.CoreData;
import com.kingtao.util.Sha256Util;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

import static com.kingtao.rpc.TestServer.logEntries;

/**
 * @Author: kingtao
 * @Date: 2023-08-31-11:34
 * @Description:
 */
@Slf4j
public class TestClient {
    public static NodeGroupServiceImpl groupService;

    //  初始化 client
    public static Channel clientInit() {
        LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);
        MessageCodec CODEC_HANDLER = new MessageCodec();
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap client = new Bootstrap();
            client.channel(NioSocketChannel.class)
                    .group(group)
                    .handler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new ProtocolFrameDecoder());
//                            channel.pipeline().addLast(LOGGING_HANDLER);
                            channel.pipeline().addLast(CODEC_HANDLER);

                            //  节点添加回馈
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<AppendNodeResponse>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, AppendNodeResponse response) throws Exception {
                                    if (response.isSuccess()) {
                                        log.debug("node {} 添加成功", response.getId());
                                    } else {
                                        log.debug("node {} 添加失败", response.getId());
                                    }
                                }
                            });
                            // 共识回馈
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<ServerInitResponse>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, ServerInitResponse response) throws Exception {
                                    if (response.isSuccess()) {
                                        log.debug("leaderId：{}，secondLeader：{}",response.getLeaderId(), response.getSecondLeaderId());
                                    }
                                }
                            });
                            //  leader节点写入日志
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<ClientResponse>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext channelHandlerContext, ClientResponse response) throws Exception {
                                    //  leader 记录失败
                                    if (response.isSuccess()) {
                                        log.debug("leader作恶，重新选取");
                                    }
                                }
                            });
                            // secondLeader 写入
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<AppendSecondLeaderLogEntryRequest>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, AppendSecondLeaderLogEntryRequest request) throws Exception {
                                    LogServiceImpl logService = new LogServiceImpl(groupService.getSecondLeader());
                                    LogEntry logEntry = new LogEntry(request.getStr());
                                    logService.writeLogEntry(logEntry);

                                    if (logService.getLastIndex() == groupService.getLeader().getLastLogIndex()) {
                                        if (Sha256Util.getSHA256(logEntry.getValue()).equals(request.getLocalIndex())) {
                                            log.debug("secondLeader 写入成功");
                                            groupService.rewardNode(groupService.getSecondLeader().getId());
                                            logService.destroy();
                                            ctx.writeAndFlush(new AppendLeaderLogEntryResponse(true));
                                        } else {
                                            //  写入失败
                                            logService.updateLastIndexKey(logService.getLastIndex() - 1);
                                            logService.destroy();
                                            boolean flag = true;
                                            while (flag) {
                                                log.debug("secondLeader 不诚实, 重新筛选");
                                                groupService.punishNode(groupService.getSecondLeader().getId());
                                                groupService.setSecondLeader();
                                                LogServiceImpl newLogService = new LogServiceImpl(groupService.getSecondLeader());
                                                newLogService.writeLogEntry(logEntry);
                                                if (newLogService.getValueFromIndexByKey(logService.getLastIndex(), 1).equals(request.getStr())) {
                                                    flag = false;
                                                    groupService.rewardNode(groupService.getSecondLeader().getId());
                                                    log.debug("新的secondLeader 写入成功");
                                                    ctx.writeAndFlush(new AppendLeaderLogEntryResponse(true));
                                                }
                                                newLogService.destroy();
                                            }
                                        }
                                    } else {
                                        //  同步日志
                                        logService.updateAllLog(logEntries);
                                    }
                                    //  分组处理
                                    if (groupService.isCreateGroup()) {
                                        //  followerLeader写入
                                        for (int i = 0; i < CoreData.GROUP_NUM; i++) {
                                            Integer followerLeaderId = groupService.getNodeGroup().getFollowerLeaderIdMap().get(0);
                                            //  todo
                                            Channel followerLeaderChannel = GroupSessionFactory.getSession().getChannel(followerLeaderId);
                                            followerLeaderChannel.writeAndFlush(new AppendFollowLeaderLogEntryRequest(followerLeaderId, request.getLocalIndex(), request.getStr()));
                                        }
                                    } else {
                                        //  未分组处理
                                        List<Node> allFollower = groupService.getNodeGroup().getAllFollower();
                                        for (Node follower : allFollower) {
                                            Channel followerChannel = GroupSessionFactory.getSession().getChannel(follower.getId());
                                            followerChannel.writeAndFlush(new AppendFollowerLogEntryRequest(follower.getId(), request.getLocalIndex(), request.getStr()));
                                        }
                                    }
                                }
                            });
                            //  followerLeader写入
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<AppendFollowLeaderLogEntryRequest>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, AppendFollowLeaderLogEntryRequest request) throws Exception {
                                    Node followerLeader = groupService.getNodeGroup().getNodeByNodeId(request.getId());
                                    LogServiceImpl logService = new LogServiceImpl(followerLeader);
                                    LogEntry logEntry = new LogEntry(request.getStr());
                                    logService.writeLogEntry(logEntry);
                                    //  所属小组编号
                                    int groupNum = request.getId() % CoreData.GROUP_NUM;

                                    if (followerLeader.getLastLogIndex() == groupService.getSecondLeader().getLastLogIndex()) {
                                        if (Sha256Util.getSHA256(logEntry.getValue()).equals(request.getStr())) {
                                            log.debug("follower 写入成功");
                                            groupService.rewardNode(request.getId());
                                            followerLeader.setLastLogIndex(logService.getLastIndex());
                                            logService.destroy();
                                        }else {
                                            logService.updateLastIndexKey(logService.getLastIndex()-1);
                                            logService.destroy();
                                            boolean flag = true;
                                            while (flag) {
                                                log.debug("follower 作恶，重新选择");
                                                groupService.punishNode(followerLeader.getId());
                                                groupService.setFollowerLeaderById(followerLeader.getId());
                                                int followerLeaderId = groupService.getNodeGroup().getFollowerLeaderIdMap().get(groupNum);
                                                followerLeader = groupService.getNodeGroup().getNodeByNodeId(followerLeaderId);
                                                LogServiceImpl newLogService = new LogServiceImpl(followerLeader);
                                                newLogService.writeLogEntry(logEntry);
                                                if (newLogService.getValueFromIndexByKey(logService.getLastIndex(), 1).equals(request.getStr())) {
                                                    flag = false;
                                                    log.debug("follower 写入成功");
                                                    groupService.rewardNode(followerLeaderId);
                                                    followerLeader.setLastLogIndex(logService.getLastIndex());
                                                    newLogService.destroy();
                                                }
                                            }
                                        }
                                    }else {
                                        logService.updateAllLog(logEntries);
                                    }
                                    //  传入follower
                                    for (int i = groupNum; i < groupService.getNodeGroup().getNodes().size(); i=i+CoreData.GROUP_NUM){
                                        Node node = groupService.getNodeGroup().getNodeByNodeId(i);
                                        if (node.getStatus() == NodeStatusEnums.follower){
                                            Channel followerChannel = GroupSessionFactory.getSession().getChannel(node.getId());
                                            followerChannel.writeAndFlush(new AppendFollowerLogEntryRequest(node.getId(), request.getLocalIndex(), request.getStr()));
                                        }
                                    }
                                }
                            });
                        }
                    });

            ChannelFuture future = client.connect(CoreData.NODE_ADDRESS, CoreData.NODE_INIT_PORT).sync();
            return future.sync().channel();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

}
